{
  // 怎么在前端做个完美的计时器？
  // [✅] 能准确的计时
  // [✅] 能够稳定的更新 UI
  // [✅] 不要浪费资源
  //
  // 法一 最基础的 浏览器 方法setInterval.
  function timer1() {
    let seconds = 1;

    setInterval(() => {
      updateUI(seconds++);
    }, 1000);
  }
  timer1();
  // [❌ ] 能精准的计时 (不能满足这个需求，下面解释)
  // [✅] 能够稳定的更新 UI (绝大部分情况能更新 UI， 这个满足)
  // [✅] 不要浪费资源 (一秒运行一次，没有浪费资源的)

  // 🍌🍌🍌🍌🍌
  // 既然 setInterval的 delay 不能精确计时，我们可以自己使用Date.now(),然后自己算出已经过去了多少秒，这样就能精确的计时。只让setInterval去更新 UI。
  // 发二 改进版 setInterval
  function timer1() {
    let start = Date.now();
    setInterval(() => {
      let gaps = Date.now() - start;
      let seconds = Math.floor(gaps / 1000);
      updateUI(seconds);
    }, 1000);
  }
  timer1();
  // [✅] 能精准的计时 （满足，因为我们自己计时）
  // [❌ ] 能够稳定的更新 UI （ 绝大部分情况能稳定的更新 UI，但是有时候会跳数).
  // [✅] 不要浪费资源 （一秒运行一次，没有浪费资源的)
  // 为什么有时候会跳数呢？因为setInterval每执行一次 callback 都有几毫秒的延迟，这就会导致，我们计时器 UI 会每一段时间都会跳数。比如突然从 255 跳到 257.

  // 🍌🍌🍌🍌🍌
  // 重绘之前调用，甚至可以每秒 60 次，和刷新率一样，看起来不错，可以解决 UI 延迟更新的问题。 与此同时我们 UI 的更新的精确度，来到了 16.67ms。
  // 法三 requestAnimationFrame
  let start = Date.now();
  function timer1() {
    let gaps = Date.now() - start;
    let seconds = Math.floor(gaps / 1000);
    updateUI(seconds);
    requestAnimationFrame(timer1);
  }
  timer1();
  // [✅] 能精准的计时
  // [✅] 能够稳定的更新 UI
  // [❌ ] 不要浪费资源 （不满足)
  // 因为每秒调用 60 次，然后更新 dom，但是我们计时器是 1 秒更新一次 UI，这就意味着浪费了 59 次。会浪费多余电力，不环保啊。
  // 怎么解决呢？可不可以不让 requestAnimationFrame 不 60 秒调用一次呢？对你想的没错，setTimeout.

  // 🍌🍌🍌🍌🍌
  // 法四 setTimeout / requestAnimationFrame (RAF)
  // 我们可以在requestAnimationFrame callback 里面不直接调用requestAnimationFrame，而是用setTimeout 自己调度。
  // 但是setTimeout delay 应该设置多少呢？我们必须保证每秒间隔内运行一次requestAnimationFrame。这就需要我们自己计算间隔.
  const start = document.timeline
    ? document.timeline.currentTime
    : performance.now(); // 取当前时间，这个时间是tab开启的时间

  function timer1(time) {
    const gaps = time - start;
    const seconds = Math.round(gaps / 1000);
    updateUI(seconds);
    setTimeout(
      () => {
        requestAnimationFrame(timer1); // requestAnimationFrame 执行回调函数的时刻 当作参数，传入到callback
        //timer1(document.timeline.currentTime) //
      },
      1000 - (gaps % 1000) // 为了保证在每秒起点开始运行requestAnimationFrame
    );
  }
  timer1();
  // 这里还是不够好，因为有些情况会出现setTimeout 执行两次的情况。

  // 🍌🍌🍌🍌🍌
  // 完美版本
  function interval(ms, callback) {
    const start = document.timeline
      ? document.timeline.currentTime
      : performance.now();
    function timer1(time) {
      const gaps = time - start;
      const seconds = Math.round(gaps / ms);
      callback(seconds);
      console.log(seconds);
      const targetNext = (seconds + 1) * ms + start; // 算出下次interval开始的时间
      const delay = document.timeline
        ? document.timeline.currentTime
        : performance.now(); // 取出更新完UI的时间
      setTimeout(
        () => {
          requestAnimationFrame(timer1); // requestAnimationFrame 执行回调函数的时刻 当作参数，传入到callback
        },
        targetNext - delay // 算出距离下次interval开始时间
      );
    }
    timer1(start);
  }

  interval(1000, updateUI);
}
